// Please see the documentation in index.html

unit Unit1;

interface

{$IFNDEF SAX_WIDESTRINGS}
This demo requires that you activate SAX_WIDESTRINGS within the Project | Options
Conditional Defines section (after you specify the flag, build all)
{$ENDIF}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ImgList, SAXComps, SAX, BSAX;

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    Label1: TLabel;
    Button1: TButton;
    ListBox2: TListBox;
    Label2: TLabel;
    Label3: TLabel;
    SAXErrorHandler1: TSAXErrorHandler;
    SAXContentHandler1: TSAXContentHandler;
    OpenDialog1: TOpenDialog;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure SAXErrorHandler1Error(Sender: TObject;
      const Error: ISAXParseError);
    procedure SAXErrorHandler1FatalError(Sender: TObject;
      const Error: ISAXParseError);
    procedure SAXErrorHandler1Warning(Sender: TObject;
      const Error: ISAXParseError);
    procedure Button1Click(Sender: TObject);
    procedure SAXContentHandler1StartElement(Sender: TObject;
      const NamespaceURI, LocalName, QName: WideString;
      const Atts: IAttributes);
    procedure ListBox2DblClick(Sender: TObject);
    procedure SAXContentHandler1SetDocumentLocator(Sender: TObject;
      const Locator: ILocator);
  private
    CurrLocator : ILocator;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses SAXAdapters;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Fill the list
  ListSAXVendors(ComboBox1.Items);
  // Set the item index
  ComboBox1.ItemIndex:= ComboBox1.Items.IndexOf(DefaultSAXVendor);
end;

procedure TForm1.SAXErrorHandler1Error(Sender: TObject;
  const Error: ISAXParseError);
begin
  // In the errors we can use the locator info or the info that is sent
  // as part of the ISAXParseError. The line and column numbers are generally
  // be the same-- but may be slightly different depending on the error
  ListBox2.Items.AddObject('[Error] ' + Error.getMessage + ' Line ' +
    IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber),
    Pointer(Error.getLineNumber));
end;

procedure TForm1.SAXErrorHandler1FatalError(Sender: TObject;
  const Error: ISAXParseError);
begin
  // In the errors we can use the locator info or the info that is sent
  // as part of the ISAXParseError. The line and column numbers are generally
  // be the same-- but may be slightly different depending on the error
  ListBox2.Items.AddObject('[Fatal Error] ' + Error.getMessage + ' Line ' +
    IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber),
    Pointer(Error.getLineNumber));
end;

procedure TForm1.SAXErrorHandler1Warning(Sender: TObject;
  const Error: ISAXParseError);
begin
  // In the errors we can use the locator info or the info that is sent
  // as part of the ISAXParseError. The line and column numbers are generally
  // be the same-- but may be slightly different depending on the error
  ListBox2.Items.AddObject('[Warning] ' + Error.getMessage + ' Line ' +
    IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber),
    Pointer(Error.getLineNumber));
end;

procedure TForm1.Button1Click(Sender: TObject);
var Stream : TMemoryStream;
    Input : IInputSource;
    XMLReader : IXMLReader;
    XMLBufReader: IBufferedXMLReader;
    Vendor: TSAXVendor;
    ContentHandler: IContentHandler;
begin
  ListBox1.Items.Clear;
  Listbox2.Items.Clear;
  CurrLocator:= nil;
  ContentHandler:= SAXContentHandler1;
  if (OpenDialog1.Execute) then
  begin
    // Load the text into the memo
    ListBox1.Items.LoadFromFile(OpenDialog1.FileName);
    // Creating a memory stream to store the document might be bad because it
    // requires that we load the entire document into memory. If you have a
    // document that is 1 gigabyte large you may run out of memory. Of course, this
    // demo assumes you can load the whole document into the memo to begin with,
    // so it is unlikely you will be using a multi megabyte file.
    Stream:= TMemoryStream.Create;
    ListBox1.Items.SaveToStream(Stream);
    // We must reset the stream!
    Stream.Seek(0, 0);
    // Now we can create a StreamInputSource. We don't need to free the
    // Stream we are passing to it.
    Input:= TStreamInputSource.Create(Stream) as IStreamInputSource;
    // Get the Default SAX Vendor and XML Reader
    Vendor:= GetSAXVendor(ComboBox1.Items[ComboBox1.ItemIndex]);
    if Vendor is TBufferedSAXVendor then
    begin
      XMLBufReader:= TBufferedSAXVendor(Vendor).BufferedXMLReader;
      XMLBufReader.setContentHandler(Adapt(ContentHandler, XMLBufReader));
      XMLBufReader.setErrorHandler(SAXErrorHandler1);
      // This time we send the InputSource we created
      XMLBufReader.parse(Input);
      XMLBufReader:= nil;
    end else
    begin
      XMLReader:= Vendor.XMLReader;
      XMLReader.setContentHandler(ContentHandler);
      XMLReader.setErrorHandler(SAXErrorHandler1);
      // This time we send the InputSource we created
      XMLReader.parse(Input);
      XMLReader:= nil;
    end;
    CurrLocator:= nil;
  end;
end;

procedure TForm1.SAXContentHandler1SetDocumentLocator(Sender: TObject;
  const Locator: ILocator);
begin
  // Save the locator, this happens before any other content events
  CurrLocator:= Locator;
end;

procedure TForm1.SAXContentHandler1StartElement(Sender: TObject;
  const NamespaceURI, LocalName, QName: WideString;
  const Atts: IAttributes);
begin
  // We will add an "[Element]" item to the list. We will store the
  // LineNumber in the objects property for easy access later
  ListBox2.Items.AddObject('[Element] ' + QName + ' Line ' +
    IntToStr(CurrLocator.LineNumber) + ' Column ' +
    IntToStr(CurrLocator.ColumnNumber),  Pointer(CurrLocator.LineNumber));
end;

procedure TForm1.ListBox2DblClick(Sender: TObject);
begin
  // Check if anything is selected
  if (ListBox2.ItemIndex = -1) then
    Exit;
  // Set the item index to the recorded position. Notice, we need to subtract
  // 1, because the line numbers reported by the document are "1" based and
  // the TListBox is "0" based
  ListBox1.ItemIndex:= Integer(ListBox2.Items.Objects[ListBox2.ItemIndex])-1;
end;

end.
